#include "leif.h"
#include <GLFW/glfw3.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MENU_WIDTH 400
#define MENU_HEIGHT 500
#define INPUT_SIZE 256
#define FONT_SIZE 16
#define FONT_PATH "/usr/share/fonts/truetype/noto/NotoSansNabataean-Regular.ttf"

static const char *s_original_items[] = {
    "alacritty", "thunar", "firefox", "nvim",     "feh",
    "cmus",      "mpv",    "khal",    "obsidian", "flameshot"};
static const uint32_t s_original_count =
    sizeof(s_original_items) / sizeof(s_original_items[0]);
static char **s_filtered_items = NULL;
static uint32_t s_filtered_count = 0;
static char s_input_buffer[INPUT_SIZE] = {0};
static char s_prev_input[INPUT_SIZE] = {0};
static int32_t s_selected_idx = 0;
static bool s_running = true;
static LfFont s_font;

static bool str_contains_ci(const char *haystack, const char *needle) {
  if (!needle || !*needle)
    return true;
  if (!haystack)
    return false;
  const char *h = haystack;
  const char *n = needle;
  while (*h) {
    if (tolower((unsigned char)*h) == tolower((unsigned char)*n)) {
      if (!*++n)
        return true;
    } else
      n = needle;
    h++;
  }
  return false;
}

static void update_filtered_items() {
  if (s_filtered_items) {
    for (uint32_t i = 0; i < s_filtered_count; i++)
      free(s_filtered_items[i]);
    free(s_filtered_items);
  }
  s_filtered_count = 0;
  for (uint32_t i = 0; i < s_original_count; i++) {
    if (str_contains_ci(s_original_items[i], s_input_buffer))
      s_filtered_count++;
  }
  if (!s_filtered_count) {
    s_filtered_items = NULL;
    return;
  }
  s_filtered_items = malloc(s_filtered_count * sizeof(char *));
  uint32_t idx = 0;
  for (uint32_t i = 0; i < s_original_count; i++) {
    if (str_contains_ci(s_original_items[i], s_input_buffer)) {
      s_filtered_items[idx++] = strdup(s_original_items[i]);
    }
  }
  s_selected_idx = s_filtered_count ? 0 : -1;
}

static void center_window(GLFWwindow *window) {
  int mx, my, mw, mh;
  glfwGetMonitorWorkarea(glfwGetPrimaryMonitor(), &mx, &my, &mw, &mh);
  int w, h;
  glfwGetWindowSize(window, &w, &h);
  glfwSetWindowPos(window, mx + (mw - w) / 2, my + (mh - h) / 2);
}

int main() {
  glfwInit();
  GLFWwindow *window =
      glfwCreateWindow(MENU_WIDTH, MENU_HEIGHT, "dmenu", NULL, NULL);
  glfwMakeContextCurrent(window);
  glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE);
  center_window(window);

  lf_init_glfw(MENU_WIDTH, MENU_HEIGHT, window);
  s_font = lf_load_font(FONT_PATH, FONT_SIZE);
  lf_push_font(&s_font);

  LfTheme theme = lf_default_theme();
  theme.div_props.color = (LfColor){30, 30, 30, 255};
  theme.text_props.color = LF_WHITE;
  theme.inputfield_props.color = (LfColor){50, 50, 50, 255};
  theme.inputfield_props.text_color = LF_WHITE;
  lf_set_theme(theme);

  update_filtered_items();

  while (s_running && !glfwWindowShouldClose(window)) {
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);

    lf_begin();

    vec2s menu_pos = {0, 0};
    vec2s menu_size = {(float)MENU_WIDTH, (float)MENU_HEIGHT};
    lf_div_begin(menu_pos, menu_size, true);

    lf_input_text_inl_ex(s_input_buffer, INPUT_SIZE, MENU_WIDTH - 20,
                         "Search...");
    if (strcmp(s_input_buffer, s_prev_input) != 0) {
      strncpy(s_prev_input, s_input_buffer, INPUT_SIZE - 1);
      update_filtered_items();
    }

    lf_next_line();

    if (s_filtered_count == 0) {
      lf_text("No items");
    } else {
      for (uint32_t i = 0; i < s_filtered_count; i++) {
        if (i == s_selected_idx) {
          LfUIElementProps props = theme.div_props;
          props.color = (LfColor){80, 80, 80, 255};
          lf_push_style_props(props);
        }

        vec2s item_size = {(float)MENU_WIDTH - 20, 30.0f};
        LfClickableItemState state = lf_item(item_size);
        lf_text(s_filtered_items[i]);

        if (i == s_selected_idx) {
          lf_pop_style_props();
        }

        if (state == LF_CLICKED) {
          printf("%s\n", s_filtered_items[i]);
          s_running = false;
          break;
        }

        lf_next_line();
      }
    }

    lf_div_end();
    lf_end();

    if (lf_key_went_down(GLFW_KEY_ENTER) && s_filtered_count > 0) {
      printf("%s\n", s_filtered_items[s_selected_idx]);
      s_running = false;
    }
    if (lf_key_went_down(GLFW_KEY_ESCAPE)) {
      s_running = false;
    }
    if (lf_key_went_down(GLFW_KEY_UP) && s_filtered_count > 0) {
      s_selected_idx =
          (s_selected_idx - 1 + s_filtered_count) % s_filtered_count;
    }
    if (lf_key_went_down(GLFW_KEY_DOWN) && s_filtered_count > 0) {
      s_selected_idx = (s_selected_idx + 1) % s_filtered_count;
    }

    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  if (s_filtered_items) {
    for (uint32_t i = 0; i < s_filtered_count; i++)
      free(s_filtered_items[i]);
    free(s_filtered_items);
  }
  lf_pop_font();
  lf_free_font(&s_font);
  lf_terminate();
  glfwDestroyWindow(window);
  glfwTerminate();
  return 0;
}